using System;
using System.Reflection;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using QuickTest.Core.Compilation;
using QuickTest.Services.TextRetrieval;
using QuickTest.Tests.Services.TextRetrieval;

namespace QuickTest.Tests.Core.Compilation
{
    [TestFixture]
    public class SolutionTests
    {
        #region TestData

        private const string SingleProject2005SolutionResourcePath = "QuickTest.Tests,QuickTest.Tests.Core.Compilation.Artifacts.SingleProject2005.sln";
        private const string MultipleProject2005SolutionResourcePath = "QuickTest.Tests,QuickTest.Tests.Core.Compilation.Artifacts.MultipleProject2005.sln";
        private const string SingleProject2008SolutionResourcePath = "QuickTest.Tests,QuickTest.Tests.Core.Compilation.Artifacts.SingleProject2008.sln";
        private const string MultipleProject2008SolutionResourcePath = "QuickTest.Tests,QuickTest.Tests.Core.Compilation.Artifacts.MultipleProject2008.sln";
        private const string FilePath = "c:/";
        private static readonly string FullFilePath = string.Format("{0}TestSolution.sln", FilePath);
        private const string SingleProject2008Name = "SingleProject2008/SingleProject2008.csproj";

        private static Solution GetTestSolution(string embeddedResourcePath)
        {
            var settings = new EmbeddedResourceTextRetrieverSettings(embeddedResourcePath);
            return Solution.Create(new FileUri(FullFilePath), new EmbeddedResourceTextRetriever(settings));
        }

        #endregion

        [Test]
        public void FilePath_Factory_Method_Should_Create_With_Uri_And_Initialised_FileTextRetriever_As_Default()
        {
            var fileUri = new FileUri(FullFilePath);
            Solution solution = Solution.Create(fileUri, new FileTextRetriever(fileUri));

            //check Uri and FileTextRetriever were correctly initialised.
            object uriValue = solution.GetType().GetProperty("FileUri", BindingFlags.Instance | BindingFlags.NonPublic)
                .GetValue(solution, null);
            object textRetriever = solution.GetType().GetProperty("TextRetriever", BindingFlags.Instance | BindingFlags.NonPublic)
                .GetValue(solution, null);

            Assert.That(ReferenceEquals(uriValue, fileUri), "FileUri should have been initialised");
            Assert.That(textRetriever, Is.InstanceOfType(typeof (FileTextRetriever)), "TextRetriever should have been initialised to a FileTextRetriever");


            //check that FileTextRetriever has been initialised with same FileUri
            object textRetrieverUriValue = textRetriever.GetType().GetProperty("FileUri", BindingFlags.Instance | BindingFlags.NonPublic)
                .GetValue(textRetriever, null);

            Assert.That(ReferenceEquals(textRetrieverUriValue, fileUri), "TextRetriever should have been initialised with a FileUri");
        }

        [Test]
        public void FilePath_ITextRetriever_Factory_Method_Should_Create_With_Uri_And_ITextRetriever()
        {
            var fileUri = new FileUri(FullFilePath);
            var settings = new EmbeddedResourceTextRetrieverSettings(SingleProject2005SolutionResourcePath);

            Solution solution = Solution.Create(fileUri, new EmbeddedResourceTextRetriever(settings));

            //check Uri was correctly initialised.
            object uriValue = solution.GetType().GetProperty("FileUri", BindingFlags.Instance | BindingFlags.NonPublic)
                .GetValue(solution, null);
            object textRetriever = solution.GetType().GetProperty("TextRetriever", BindingFlags.Instance | BindingFlags.NonPublic)
                .GetValue(solution, null);

            Assert.That(ReferenceEquals(uriValue, fileUri), "FileUri should have been initialised");
            Assert.That(ReferenceEquals(textRetriever, textRetriever), "TextRetriever should have been initialised");
        }

        [Test]
        public void ArgumentNullException_Thrown_If_Created_With_Null_TextRetriever()
        {
            TestUtils.AssertThrows<ArgumentNullException>(() => Solution.Create(new FileUri(FullFilePath), null), "ArgumentNullException should have been thrown.");
        }

        [Test]
        public void ArgumentNullException_Thrown_If_Created_With_Null_FileUri()
        {
            var settings = new EmbeddedResourceTextRetrieverSettings(SingleProject2005SolutionResourcePath);

            TestUtils.AssertThrows<ArgumentNullException>(() => Solution.Create(null, new EmbeddedResourceTextRetriever(settings)), "ArgumentNullException should have been thrown.");
        }

        [Test]
        public void Can_Get_Single_Project_From_2005_Solution_File()
        {
            Solution solution = GetTestSolution(SingleProject2005SolutionResourcePath);

            Assert.That(solution.Projects.Count, Is.EqualTo(1), "One project should have been returned");
        }

        [Test]
        public void Can_Get_Multiple_Projects_From_2005_Solution_File()
        {
            Solution solution = GetTestSolution(MultipleProject2005SolutionResourcePath);

            Assert.That(solution.Projects.Count, Is.EqualTo(2), "Two projects should have been returned");
        }

        [Test]
        public void Can_Get_Single_Project_From_2008_Solution_File()
        {
            Solution solution = GetTestSolution(SingleProject2008SolutionResourcePath);

            Assert.That(solution.Projects.Count, Is.EqualTo(1), "One project should have been returned");
        }

        [Test]
        public void Can_Get_Multiple_Projects_From_2008_Solution_File()
        {
            Solution solution = GetTestSolution(MultipleProject2008SolutionResourcePath);

            Assert.That(solution.Projects.Count, Is.EqualTo(2), "Two projects should have been returned");
        }

        [Test]
        public void Should_Create_Project_With_Uri_Of_Solution_Path_Plus_Project_Path()
        {
            Solution solution = GetTestSolution(SingleProject2008SolutionResourcePath);

            //check Uri was correctly initialised.
            FileUri projectFileUri = solution.Projects[0].FileUri;

            Assert.That(projectFileUri.AbsolutePath, Is.EqualTo(FilePath + SingleProject2008Name), "Uri should equal the solution directory path plus the relative project path.");
        }

        [Test]
        public void CompilationException_Thrown_If_Invalid_Solution_File()
        {
            Solution solution = GetTestSolution(EmbeddedResourceTextRetrieverTests.EmbeddedResourcePath);

            Exception ex = TestUtils.AssertThrows<CompilationException>(() => solution.Projects[0].ToString(), "CompilationException should have been thrown.");

            Assert.That(ex.Message, Is.EqualTo("Invalid solution file provided"), "Exception should be 'Invalid solution file provided'");
        }
    }
}